﻿using System.Net;
using System.Text.Json;
using System.Text;

namespace LLAntBlazor.Web.Common;

public class BfDataHttpClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly bool _disposeHttpClient;

    public BfDataHttpClient(HttpClient httpClient = null)
    {
        _httpClient = httpClient ?? new HttpClient();
        _disposeHttpClient = httpClient == null;
    }

    public async Task<HttpResponseResult> GetAsync(
        string url,
        Dictionary<string, string> headers = null,
        TimeSpan? timeout = null,
        CancellationToken cancellationToken = default)
    {
        return await SendRequestAsync(
            HttpMethod.Get,
            url,
            headers,
            content: null,
            timeout,
            cancellationToken);
    }

    public async Task<HttpResponseResult> PostAsync(
        string url,
        object data,
        Dictionary<string, string> headers = null,
        TimeSpan? timeout = null,
        CancellationToken cancellationToken = default)
    {
        var content = new StringContent(
            JsonSerializer.Serialize(data),
            Encoding.UTF8,
            "application/json");

        return await SendRequestAsync(
            HttpMethod.Post,
            url,
            headers,
            content,
            timeout,
            cancellationToken);
    }

    private async Task<HttpResponseResult> SendRequestAsync(
        HttpMethod method,
        string url,
        Dictionary<string, string> headers,
        HttpContent content,
        TimeSpan? timeout,
        CancellationToken cancellationToken)
    {
        try
        {
            using var request = new HttpRequestMessage(method, url);
            AddHeaders(request, headers);

            if (content != null)
            {
                request.Content = content;
            }

            var cts = new CancellationTokenSource();
            if (timeout.HasValue)
            {
                cts.CancelAfter(timeout.Value);
            }

            using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
                cts.Token, cancellationToken);

            var response = await _httpClient.SendAsync(
                request,
                HttpCompletionOption.ResponseHeadersRead,
                linkedCts.Token);

            var responseContent = await response.Content.ReadAsStringAsync();

            return new HttpResponseResult
            {
                IsSuccess = response.IsSuccessStatusCode,
                StatusCode = response.StatusCode,
                Content = responseContent,
                ErrorMessage = null
            };
        }
        catch (HttpRequestException ex)
        {
            return CreateErrorResult($"Request error: {ex.Message}", HttpStatusCode.ServiceUnavailable);
        }
        catch (TaskCanceledException ex) when (!cancellationToken.IsCancellationRequested)
        {
            return CreateErrorResult("Request timed out", HttpStatusCode.RequestTimeout);
        }
        catch (Exception ex)
        {
            return CreateErrorResult($"Unexpected error: {ex.Message}", HttpStatusCode.InternalServerError);
        }
    }

    private void AddHeaders(HttpRequestMessage request, Dictionary<string, string> headers)
    {
        if (headers == null) return;

        foreach (var header in headers)
        {
            request.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
    }

    private HttpResponseResult CreateErrorResult(string errorMessage, HttpStatusCode statusCode)
    {
        return new HttpResponseResult
        {
            IsSuccess = false,
            StatusCode = statusCode,
            Content = null,
            ErrorMessage = errorMessage
        };
    }

    public void Dispose()
    {
        if (_disposeHttpClient)
        {
            _httpClient?.Dispose();
        }
    }
}

public class HttpResponseResult
{
    public bool IsSuccess { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public string Content { get; set; }
    public string ErrorMessage { get; set; }

    public T GetData<T>()
    {
        return JsonSerializer.Deserialize<T>(Content);
    }
}